Best Practices
Use Typescript
State
State Should Be Immutable
React state should be treated as immutable. We should never mutate this.state directly, as calling setState() afterward may replace the mutation you made.
addNewUser = () =>{
/**
* OfCourse not correct way to insert
* new user in user list
*/
const users = this.state.users;
users.push({
userName: "robin",
email: "email@email.com"
});
setState({users: users});
}
- The concern here is that we are pushing new users onto the variable users, which is a reference to
this.state.users
.
//for array
addNewUser = () => {
setState(state => ({
users: [...state.users, {newdata}]
}));
};
//for object
addNewUser = () => {
setState(state => ({
users: {...state.users, age: newdata}
}));
};
Avoid Props in Initial States
Don't initialize state with props which can be changed later. Instead, use props directly in the component.
//before
function EditPanelComponent({applyCoupon}) {
const [applyCoupon, setapplyCoupon] = useState(applyCoupon)
return (
<div>
{applyCoupon && Enter Coupon: <Input/>}
</div>
);
}
- If the props are changed without the refreshing the component,
- the new prop value will never be assigned to the state’s
applyCoupon
.
- the new prop value will never be assigned to the state’s
- This is because the constructor function is only called when
EditPanelComponent
is first created.
//after
function EditPanelComponent({applyCoupon}) {
return (
<div>
{applyCoupon && Enter Coupon: <Input/>}
</div>
);
}
Use Memoization
- There are three ways we can achieve memorization in react
- Pure-Component for memorizing Class-Based components
- no discussed since class components are no longer recommended
- React.memo for memorizing functional components
useCallback
for memorizing function instance
- useMemo for memorizing heavy functions result
- Pure-Component for memorizing Class-Based components
React.memo : Memorize React Components
- Memo allows us to implement memorization in functional components
- It will only force the component to re-render if the props are changed.
const UserDetail = ({title, full_name, profile_img}) =>{
return (
<div className="user-detail-wrapper">
<img src={profile_img} />
<h4>{full_name}</h4>
<p>{title}</p>
</div>
)
}
export default React.memo(UserDetail);
//usage in parent component
return (
<>
<p>{counter}</p>
<UserDetail
title="Mr."
full_name="Zack"
profile_img="image.png"
/>
//other components
</>
);
- in the above examples,
UserDetail
will not re-render as long as it's props aren't changes even if the other components re-render, however React.memo has one limitation- it is unable to handle functional props, if we introduce callback functions to
userDetail
component its behavior will change
- it is unable to handle functional props, if we introduce callback functions to
//..prev code
//usage in parent component
const updateProfile = () => {
//...
}
return (
<>
<p>{counter}</p>
<UserDetail
title="Mr."
full_name="Zack"
profile_img="image.png"
onUpdateClick={updateProfile}
/>
//other components
</>
);
- based on the above new code, if you somehow we change
counter variable i.e using setCounter
UserDetail
willre-render
even tough its props hasn't changed, how can we fix thisuseCallback
is typically used to optimize the performance of child components that depend on a function prop.- By
memoizing
the function usinguseCallback
, we can prevent unnecessary re-renders of the child components that use the function, even if the function's dependencies change.
//..prev Code
//usage in parent component
const updateProfile = useCallback(() => {
//...
}, []);
// if we have a function with parameters
// const updateProfile = useCallback((a,b) => {
// //...
//}, [a,b]);
return (
<>
<p>{counter}</p>
<UserDetail
title="Mr."
full_name="Zack"
profile_img="image.png"
onUpdateClick={updateProfile}
/>
//other components
</>
);
You should always use React.memo LITERALLY, as comparing the tree returned by the Component is always more expensive than comparing a pair of props properties
useMemo : Memorize Function Result
const multiply = (x,y) => {
return x*y
}
const cachedValue = useMemo(() => multiply(x, y), [x, y])
return <p>{ cachedValue(12,12) }</p>
- The computed result is stored in the
cachedValue
variable anduseMemo()
Hook will return it each time unless the inputs are changed.
Other Useful Hooks & Concepts
useRef
- It can be used to store a mutable value that does not cause a re-render when updated.
- For states that shouldn't cause re-render
- It can be used to access a DOM element directly.
- value is stored inside current property,
{current: $value}
function App() {
const [inputValue, setInputValue] = useState("");
const count = useRef(0);
useEffect(() => {
count.current = count.current + 1;
});
return (
<>
<h1>Render Count: {count.current}</h1>
</>
);
}
- In the above example, If we tried to count how many times our application renders using the useState Hook(inside the useEffect hook), we would be caught in an infinite loop since this Hook itself causes a re-render.
useContext
- what is react-context or
useContext
?useContext
is a hook in React that enables components to consume context values created by a parent component and passed down through the component tree.- It is an alternative to the prop drilling technique used to pass down data through several layers of components.
- How to use
useContext
- create context
- provide the context at parent with modifier
Context.provide
- use the context in child components with
useContext
//create the context
import React from "react";
const CountContext = React.createContext(defaultValue);
export default CountContext;
//provide it at the parent
import React, { useState } from "react";
import Child from "./Child";
import CountContext from "./context";
const App = () => {
const [count, setCount] = useState(0);
const countHandler = () => {
setCount(count + 1);
};
return (
<CountContext.Provider value={{ count, countHandler }}>
<Child />
<h2>{count}</h2>
</CountContext.Provider>
);
};
export default App;
//use it inside a child
import { useContext } from "react";
import CountContext from "./context";
const Child = () => {
const { count, countHandler } = useContext(CountContext);
return (
<div>
<p>count is: {count}</p>
<button onClick={countHandler}>Increment</button>
</div>
);
};
export default Child;
Avoid Inline Function Definition in the Render Function.
- Since functions are objects in JavaScript
({} !== {})
, the inline function will always fail the prop diff when React does a diff check. - An arrow function will create a new instance of the function on each render if it's used in a JSX property.
- This might create a lot of work for the garbage collector.
//before
<Comment onClick={(e)=> setState({elt: e})} comment={comment} key={comment.id}/>
//after
onCommentClick = (event) => {
setState({elt:event})
}
<Comment onClick={onCommentClick} comment={comment} key={comment.id}/>
Spreading Props on DOM Elements
You should avoid spreading properties into a DOM element as it adds unknown HTML attribute, which is unnecessary and a bad practice.
<div {...props}>
{props.text}
</div>
- Instead of spreading props, you can set specific attributes:
<div witdth={props.width} hight={props.hight}>
{props.text}
</div>
React Web Workers
import React, {useEffect} from 'react';
import {Page} from '@shopify/polaris';
import {createWorkerFactory, useWorker} from '@shopify/react-web-worker';
// assume ./worker.ts contains
// export function hello(name) {
// return `Hello, ${name}`;
// }
const createWorker = createWorkerFactory(() => import('./worker'));
function Home() {
const worker = useWorker(createWorker);
const [message, setMessage] = React.useState(null);
useEffect(() => {
(async () => {
// Note: in your actual app code, make sure to check if Home
// is still mounted before setting state asynchronously!
const webWorkerMessage = await worker.hello('Tobi');
setMessage(webWorkerMessage);
})();
}, [worker]);
return <Page title="Home"> {message} </Page>;
}
Different Ways to Write CSS in React
Import Styles
import "./Components/css/App.css"; //imported styles
function App() {
return (
<div className="main">
</div>
);
}
export default App;
Write Inline Styles
<div className="main" style={{color:"red"}}>
- or construct an object of styles
function App() {
const styles = {
main: {
backgroundColor: "#f1f1f1",
width: "100%",
},
inputText: {
padding: "10px",
color: "red",
},
};
return (
<div className="main" style={styles.main}>
<input type="text" style={styles.inputText}></input>
</div>
);
}
export default App;
Use CSS Modules
- CSS Modules allows us to use the same class name in multiple files without clashes since each class name is given a unique programmatic name.
- This is especially useful in larger applications. Every class name is scoped locally to the specific component in which it is being imported.
- rule
- create a file ending with .module.css
/* styles.module.css */
.font {
color: #f00;
font-size: 20px;
}
import styles from "./styles.module.css"; //you have to import it like this
function App() {
return (
<h1 className={styles.heading}>Hello World</h1>
);
}
export default App;
- Other relevant methods
- TailwindCSS
- Styled-components